﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

namespace AssemblyExplorer
{
    public class AssemblyDataProvider : IDataProvider
    {
        private Assembly _assembly;
        private IDescriptionStrategy _descriptionStrategy;
        private BindingFlags _bindingFlags;

        public AssemblyDataProvider()
        {
            _assembly = Assembly.GetExecutingAssembly();
            _descriptionStrategy = new DefaultDescriptionStrategy();
            _bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic |
                    BindingFlags.Public | BindingFlags.Static;
        }

        public IDescriptionStrategy DescriptionStrategy
        {
            get { return _descriptionStrategy; }
            set { _descriptionStrategy = value; }
        }

        public BindingFlags BindingFlags
        {
            get { return _bindingFlags; }
            set { _bindingFlags = value; }
        }

        public virtual void LoadAssembly(string assemblyFile)
        {
            _assembly = Assembly.LoadFrom(assemblyFile);
        }

        public virtual DataItem GetRootDataItem()
        {
            return new AssemblyDataItem(_assembly.FullName, "Assembly", _assembly);
        }

        public virtual IEnumerable<DataItem> GetChildDataItems(DataItem item)
        {
            string typeName = item.ReferencedValue.GetType().Name;
            if (typeName == "Assembly")
            { 
                Assembly assembly = item.ReferencedValue as Assembly;
                foreach (Type type in assembly.GetTypes())
                {
                    yield return new AssemblyDataItem(type.Name, "Class", type);
                }
            }
            else if (typeName == "RuntimeType")
            {
                Type type = item.ReferencedValue as Type;
                if (type.BaseType != null)
                {
                    yield return new AssemblyDataItem(type.BaseType.Name, "Class", type.BaseType);
                }
                foreach (Type interf in type.GetInterfaces())
                {
                    yield return new AssemblyDataItem(interf.Name, "Interface", interf);
                }
            }
        }

        public virtual IEnumerable<DataItem> GetListDataItems(DataItem item)
        {
            if (item.ReferencedValue.GetType().Name == "RuntimeType")
            { 
                Type type = item.ReferencedValue as Type;
                BindingFlags bf = _bindingFlags;

                foreach(ConstructorInfo ci in type.GetConstructors(bf))
                    yield return new AssemblyDataItem(ci.Name, "Method", ci);

                foreach(MethodInfo mi in type.GetMethods(bf))
                    yield return new AssemblyDataItem(mi.Name, "Method", mi);

                foreach(PropertyInfo mi in type.GetProperties(bf))
                    yield return new AssemblyDataItem(mi.Name, "Properties", mi);

                foreach(FieldInfo mi in type.GetFields(bf))
                    yield return new AssemblyDataItem(mi.Name, "Field", mi);

                foreach(EventInfo mi in type.GetEvents(bf))
                    yield return new AssemblyDataItem(mi.Name, "Event", mi);
            }
        }

        public virtual string GetDescription(DataItem item)
        {
            string typeName = item.ReferencedValue.GetType().Name;
            if (typeName == "Assembly")
            {
                return _descriptionStrategy.GetAssemblyDescription((Assembly)item.ReferencedValue);
            }
            else if (typeName == "RuntimeType")
            {
                return _descriptionStrategy.GetTypeDesription((Type)item.ReferencedValue);
            }
            else if (typeName == "RuntimeMethodInfo")
            {
                return _descriptionStrategy.GetMethodDescription((MethodInfo)item.ReferencedValue);
            }
            else if (typeName == "RuntimeConstructorInfo")
            {
                return _descriptionStrategy.GetConstructorDescription((ConstructorInfo)item.ReferencedValue);
            }
            else if (typeName == "RuntimePropertyInfo")
            {
                return _descriptionStrategy.GetPropertyDescription((PropertyInfo)item.ReferencedValue);
            }
            else if (typeName == "RtFieldInfo")
            {
                return _descriptionStrategy.GetFieldDescription((FieldInfo)item.ReferencedValue);
            }
            else if (typeName == "RuntimeEventInfo")
            {
                return _descriptionStrategy.GetEventDescription((EventInfo)item.ReferencedValue);
            }
            else
            {
                return typeName;
            }
        }
    }
}
